package kassiopeia.agent.bootstrap;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import kassiopeia.agent.bootstrap.logger.SimpleLogger;

public class Bootstrap {

    private static final SimpleLogger logger = SimpleLogger.getLogger(Bootstrap.class);
    public static final String SHARED_LIB_DIR_PROPERTY_KEY = "agent.shared";
    public static final String DISTR_LIB_DIR_PROPERTY_KEY = "agent.distribution";
    public static final String LIB_DIR_PROPERTY_KEY = "agent.lib";
    public static final String CONF_DIR_PROPERTY_KEY = "agent.conf";

    // Kassiopeia class loaders
    private ClassLoader sharedClassLoader;
    private ClassLoader agentClassLoader;
    private static Bootstrap daemon;

    public static void main(String[] args) {

        Thread.currentThread().setName("Kassiopeia Main Thread");

        String version = "";
        Package p = Package.getPackage("kassiopeia.agent");
        if (p != null) {
            version = ": " + p.getImplementationVersion();
        }

        System.out.println();
        System.out.println("***********************************************************************");
        System.out.println("\nKassiopeia, Version" + version + "\n");
        System.out.println("***********************************************************************");
        System.out.println();

        if (daemon == null) {
            daemon = new Bootstrap();
            try {
                logger.info("Initializing Kassiopeia");
                daemon.init();
                logger.infoInBrackets("Initialization Successful");

                logger.info("Starting Kassiopeia agent");
                daemon.start();
                logger.infoInBrackets("Agent started");

            } catch (Exception t) {
                logger.errorInBrackets(t.toString());
            }
        }
    }

    private void init() throws Exception {

        printClassPath("SYSTEM", Thread.currentThread().getContextClassLoader());

        String sharedDir = System.getProperty(SHARED_LIB_DIR_PROPERTY_KEY);
        String distrDir = System.getProperty(DISTR_LIB_DIR_PROPERTY_KEY);
        String libDir = System.getProperty(LIB_DIR_PROPERTY_KEY);
        String confDir = System.getProperty(CONF_DIR_PROPERTY_KEY);

        File[] sharedLocations = new File[]{new File(sharedDir)};
        sharedClassLoader = ClassLoaderFactory.createClassLoader("SHARED", sharedLocations, null);

        File[] agentPacked = new File[]{new File(distrDir), new File(libDir)};
        File[] agentUnpacked = new File[]{new File(confDir)};
        agentClassLoader = ClassLoaderFactory.createClassLoader("AGENT", agentUnpacked, agentPacked, sharedClassLoader);
    }

    private void start() throws Exception {

        Thread.currentThread().setContextClassLoader(agentClassLoader);

        logger.info("Loading startup class");

        Class<?> startupClass = agentClassLoader.loadClass("kassiopeia.agent.KassiopeiaSpringRunner");
        Object startupInstance = startupClass.newInstance();

        startupClass.getField("sharedClassLoader").set(startupInstance, sharedClassLoader);

        startAndUnpackExceptions(startupInstance);
    }

    private void startAndUnpackExceptions(Object startupInstance) throws Exception {
        try {
            Method method = startupInstance.getClass().getMethod("startKassiopeiaAgent", new Class[0]);
            method.invoke(startupInstance, new Object[0]);
        } catch(InvocationTargetException exception) {
            throw new RuntimeException(exception.getTargetException());
        }
    }

    private void printClassPath(String loaderName, ClassLoader classLoader) {

        if (!(classLoader instanceof URLClassLoader)) {
            return;
        }
        URLClassLoader urlClassLoader = (URLClassLoader) classLoader;

        logger.info("Classpath for " + loaderName + " class loader");
        for (URL url : urlClassLoader.getURLs()) {
            logger.info(url.toString());
        }
    }
}
